class Compile {
    constructor(el, vm) {
        this.el = typeof el === 'string' ? document.querySelector(el) : el;
        this.vm = vm;
        if (this.el) {
            let fragment = this.nodeToFragment(this.el);
            this.compile(fragment)
            this.el.appendChild(fragment)
        }

    }

    nodeToFragment(node) {
        let fragment = document.createDocumentFragment();
        let childNodes = node.childNodes;
        this.toArray(childNodes).forEach(e => {
            fragment.appendChild(e)
        })
        return fragment

    }

    compile(fragment) {
        this.toArray(fragment.childNodes).forEach(node => {
            if (this.isElementNode(node)) {
                this.compileElement(node)
            } else {
                this.compileText(node)
            }
            if (node.childNodes && node.childNodes.length > 0) {
                this.compile(node)
            }
        });
    }

    toArray(arrayLike) {
        return [].slice.call(arrayLike)
    }

    isElementNode(node) {
        return node.nodeType === 1;
    }

    compileElement(node) {
        this.toArray(node.attributes).forEach(attr => {
            let name = attr.name;
            let number = this.isDirective(name);
            if (number) {
                let type = name.slice(number);
                if (compileUtil.hasOwnProperty(type)) {
                    compileUtil[type](node, this.vm, attr.value)
                } else {
                    compileUtil.eventHandler(node, this.vm, type, attr.value)
                }

            }
        })
    }

    compileText(node) {
        let content = node.textContent;
        let reg = /\{\{(.+)\}\}/
        if (reg.test(content)) {
            let expr = RegExp.$1;
            node.textContent = content.replace(reg, compileUtil.getRealData(this.vm, expr))

            new Watcher(this.vm, expr, newVal=>{
                node.textContent = content.replace(reg, newVal)
            })
        }
    }

    isDirective(str) {
        let res = 0;
        if (str.startsWith('v-on:')) {
            res = 5;
        } else if (str.startsWith('v-')) {
            res = 2;
        } else if (str.startsWith('@')) {
            res = 1;
        }
        return res;
    }
}

let compileUtil = {
    text(node, vm, expr) {
        node.textContent = this.getRealData(vm, expr)
        new Watcher(vm, expr, newVal=>{
            node.textContent = newVal
        })
    },
    html(node, vm, expr) {
        node.innerHTML = this.getRealData(vm, expr)
        new Watcher(vm, expr, newVal=>{
            node.innerHTML = newVal
        })
    },
    model(node, vm, expr) {
        node.value = this.getRealData(vm, expr)
        new Watcher(vm, expr, newVal=>{
            node.value = newVal
        })
    },
    eventHandler(node, vm, type, val) {
        if (vm.$methods.hasOwnProperty(val)) {
            node.addEventListener(type, vm.$methods[val].bind(vm))
        }
    },
    getRealData(vm, expr) {
        let resolved = vm.$data
        expr.split('.').forEach(key => {
            resolved = resolved[key]
        })
        return resolved
    }
}
